var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var uuid = require('node-uuid');
var Session = require('express-session');
var RedisStore = require('connect-redis')(Session);
var redisOptions = {
       db: 1,
       host: "127.0.0.1"
};
var secret = 'opdihf23#*U*RUWE3[98rh32pOFSDJ{)W*(#';
var session = Session({
	store: new RedisStore(redisOptions),
	secret: secret,
	resave: true,
	saveUninitialized: true
});
var Redis = require('ioredis');
var redis = new Redis();
var request = require('request');
var rp = require('request-promise');
var cheerio = require('cheerio');
var randstr = require('randomstring');
var crc32 = require('crc32');

io.use(function(socket,next){
	session(socket.handshake, {}, next);
});

var listen_port = 9999;
server.listen( listen_port, function(){
	console.log((new Date()).toString() + 'Start listening to port '+listen_port);
} );


io.on('connection', function( socket ){
	socket.on('disconnect', function(){
		var stat = socket.handshake.session.stat || '';
		if( stat == 'sharing' ){


		var uid = socket.handshake.session.uid;
		redis.hget(room(uid), 'channel').then(function(result){
			delete socket.handshake.session.uid;
			delete socket.handshake.session.channel;
			socket.handshake.session.stat = '';
			socket.handshake.session.save();
			//console.log((new Date()).toString() + 'stopshare hexists channel: '+result);

			if( !result ){
				return new Error('Uid '+uid+' is not casting.');
			}
			return result;
		})
		.then( function( roomId ){
			var channelName = channel(roomId);
			socket.broadcast.to(channelName).emit('player.stopShare');
			socket.leave(channelName);
			//console.log((new Date()).toString() + 'User ' + uid + ' stoped sharing playlist in channel '+roomId+' .');
			redis.del(room(uid));
			redis.del(audience(uid));
		})
		.catch( function(e){
			console.log((new Date()).toString() + e);
		});






			//console.log((new Date()).toString() +  socket.handshake.session.uid + ' has stop shating caused by disconnection' );
		}
		else if ( stat == 'listening' ){



			var uid = socket.handshake.session.uid;
			var casterUid = socket.handshake.session.listeningToUser;
			var channelName = socket.handshake.session.channelName;

			redis.hgetall( room(casterUid) )
			.then(function( roomInfo ){
				socket.handshake.session.stat = '';
				socket.handshake.session.save();
				socket.leave( channelName );
				redis.hdel( audience(casterUid), uid );
				io.to(channelName).emit('user.audienceLeft', { uid: uid });

console.	log( 'stopListen'+JSON.stringify(roomInfo));
				delete socket.handshake.session.listeningToUser;
				delete socket.handshake.session.channelName;
				console.log((new Date()).toString() + 'user stop listening to uid:' + casterUid);
			})



			console.log((new Date()).toString() +  socket.handshake.session.uid + 'has stop listened caused by disconnection' );
		}
		else{
			console.log((new Date()).toString() + 'user has just disconnected');
		}
	});

	socket.on('user.startShare', function( data ){
		var uid = data.uid;
		if( !uid ){
			return false;
		}
		redis.hget(room(uid), 'channel').then(function(roomId){
			if( !roomId ){
				console.log((new Date()).toString() + 'roomId was not existed');
				roomId = uuid.v4();
			}
			return roomId;
		})
		.then( function( roomId ){
			var now = (new Date()).getTime();
			var pasttime = Math.max( parseInt(Math.abs(data.passtime*1000)), 0 );
			var startTime = now - pasttime;
			var _r_uid = room(uid);
			redis.hset(_r_uid,'channel',roomId);
			redis.hset(_r_uid,'songId', data.songId);
			redis.hset(_r_uid,'startTime', startTime);
			redis.hset(_r_uid,'socket_id', socket.id);
			redis.expire( _r_uid, 30*60 ); //30minutes

			console.log((new Date()).toString() + 'User ' + socket.handshake.session.uid + ' started sharing playlist in channel  '+roomId+' .');
			socket.join(channel( roomId ));
			socket.handshake.session.channelName = channel( roomId );
			return _xm_userinfo(uid);
		})
		.then(function( result ){
		console.log((new Date()).toString() + ' start share userinfo'+JSON.stringify(result));
			socket.handshake.session.uid = uid;
			socket.handshake.session.nickname = result.nickname;
			socket.handshake.session.stat = 'sharing';
			socket.handshake.session.save();
		})
		.catch( function(e){
			console.log((new Date()).toString() + e);
		});
	});

	socket.on('user.stopShare', function(){
		var uid = socket.handshake.session.uid;
		redis.hget(room(uid), 'channel').then(function(result){
			delete socket.handshake.session.uid;
			delete socket.handshake.session.channel;
			socket.handshake.session.stat = '';
			socket.handshake.session.save();
			console.log((new Date()).toString() + 'stopshare hexists channel: '+result);

			if( !result ){
				return new Error('Uid '+uid+' is not casting.');
			}
			return result;
		})
		.then( function( roomId ){
			var channelName = channel(roomId);
			socket.broadcast.to(channelName).emit('player.stopShare');
			socket.leave(channelName);
			console.log((new Date()).toString() + 'User ' + uid + ' stoped sharing playlist in channel '+roomId+' .');
			redis.del(room(uid));
			redis.del(audience(uid));
		})
		.catch( function(e){
			console.log((new Date()).toString() + e);
		});

	});

	socket.on('user.startListen', function( data, fn ){
		var castUid = data.casterUid;

		Promise.all([
			_xm_userinfo( data.uid ),
			redis.hgetall( room(castUid) ),
			redis.hgetall( audience(castUid) )
		])
		.then(function( result ){
			var roomInfo = result[1];
			if( isEmptyObject( roomInfo ) ){
				throw new Error('user is not casting');
			}

			var roomId = roomInfo.channel;
			var songId = roomInfo.songId;
			var startTime = roomInfo.startTime;
			var socketId = roomInfo.socket_id;
			var passtimeMS = ((new Date()).getTime() - startTime);
			var channelName = channel(roomId);

			socket.handshake.session.uid = result[0].uid;
			socket.handshake.session.nickname = result[0].nickname;
			socket.handshake.session.listeningToUser = castUid;
			socket.handshake.session.channelName = channelName;
			socket.handshake.session.stat = 'listening';
			socket.handshake.session.save();
			console.log((new Date()).toString() +  'caster started at: '+startTime );

			socket.join( channelName );
	 		redis.hset( audience(castUid), result[0].uid, JSON.stringify(result[0]) );
			console.log((new Date()).toString() + 'user started to listen uid:' + castUid + ' in '+channelName);

			var audienceData = {
				uid: result[0].uid,
				nickname: result[0].nickname,
				avatar: result[0].avatar
			};
			socket.broadcast.to(channelName).emit('user.newAudience', audienceData);

console.log((new Date()).toString() + 'room already has audience:'+JSON.stringify(result[2]));
			var audiences = result[2];
			audiences[result[0].uid] = JSON.stringify(audienceData);
			var sendData = {
				errCode: 0,
				errMsg: '',
				data:{
					songId: songId,
					passtime: passtimeMS / 1000,
					audiences: result[2]
				}
			};
			fn( sendData );
		})
		.catch(function(e){
			var sendData = {
				errCode: 301,
				errMsg: '用户ID：'+castUid+' 没有开直播间哦~'
			};
			fn( sendData );
			console.log((new Date()).toString() +  e );
			console.log((new Date()).toString() + 'Failed to get roomId of uid: ' + castUid);
		});
	});

	socket.on('user.stopListen', function(){
		var uid = socket.handshake.session.uid;
		var casterUid = socket.handshake.session.listeningToUser;
		var channelName = socket.handshake.session.channelName;

		redis.hgetall( room(casterUid) )
		.then(function( roomInfo ){
			socket.handshake.session.stat = '';
			socket.handshake.session.save();
			socket.leave( channelName );
			redis.hdel( audience(casterUid), uid );
			socket.broadcast.to(channelName).emit('user.audienceLeft', { uid: uid });

console.log((new Date()).toString() +  'stopListen'+JSON.stringify(roomInfo));
			delete socket.handshake.session.listeningToUser;
			delete socket.handshake.session.channelName;
			console.log((new Date()).toString() + 'user stop listening to uid:' + casterUid);
		})
	});

	socket.on('player.playSong', function( data ){
		var uid = socket.handshake.session.uid;
		var channelName = socket.handshake.session.channelName;

		socket.broadcast.to(channelName).emit('player.playSong', 'caster played current song');
		console.log((new Date()).toString() +  uid + ' has played song');
	});

	socket.on('player.pauseSong', function( data ){
		var uid = socket.handshake.session.uid;
		var channelName = socket.handshake.session.channelName;

		socket.broadcast.to(channelName).emit('player.pauseSong', 'caster paused current song');
		console.log((new Date()).toString() +  uid + ' has paused song');
	});
	socket.on('player.switchSong', function( data ){
		var uid = socket.handshake.session.uid;
		var channelName = socket.handshake.session.channelName;

		var now = (new Date()).getTime();
		var pasttime = Math.max( parseInt(Math.abs(data.passtime*1000)), 0 );
		var startTime = now - pasttime;
		var _r_uid = room( uid );
		redis.hset(_r_uid,'songId', data.songId);
		redis.hset(_r_uid,'startTime', startTime);

		socket.broadcast.to( channelName ).emit('player.switchSong', data );
		console.log((new Date()).toString() +  uid + ' share playlist: '+ JSON.stringify(data) );
		redis.expire( _r_uid, 30*3600 );
	});

	socket.on('user.isCasting', function( data, fn ){
		var uid = data.uid;
		redis.exists(room(uid))
		.then(function( result ){
			fn( { data:{isCasting: result }} );
		})
		.catch(function(e){
			console.log((new Date()).toString() + e);
		});
	});

	socket.on('chat.message', function( data ){
		var content = data.content;
		var uid = socket.handshake.session.uid;
		var nickname = socket.handshake.session.nickname;
		var channelName = socket.handshake.session.channelName;
		socket.broadcast.to(channelName).emit('chat.message',{uid:uid,nickname: nickname,content: content});
	});

});

io.on('disconnect', function( socket ){
	socket.handshake.session.destroy();
	console.log((new Date()).toString() + 'User has disconnected');
});

// redis.on('message', function( channel, message ){
// 	console.log((new Date()).toString() + 'Message received: ' + message);
// });

function room(uid){
	return _redis_get_room_key_by_uid(uid);
}
function _redis_get_room_key_by_uid( uid ){
	return 'roomInfo_'+uid;
}

function userinfo( uid ){
	return _redis_get_room_key_by_uid(uid);
}
function _redis_get_user_info_key_by_uid( uid ){
	return 'userInfo_'+uid;
}

function channel( roomId ){
	return _socket_get_channel_name_by_roomId(roomId);
}
function _socket_get_channel_name_by_roomId( roomId ){
	return 'channel-'+roomId;
}

function audience( uid ){
	return _redis_get_audience_list_key_by_roomId(uid);
}
function _redis_get_audience_list_key_by_roomId( uid ){
	return 'audience_'+uid;
}

function _xm_userinfo( uid ){
	if( !uid ){
		var fake_uid = randstr.generate({
			length: 8,
			charset: 'numeric'
		});
		return {
			uid: 'u_'+fake_uid,
			avatar: 'http://img.xiami.net/res/img/default/usr100.gif',
			nickname: 'guest_'+fake_uid
		}
	}
	var opt = {
		uri:'http://www.xiami.com/u/'+ uid ,
		simple: true,
		transform2xxOnly: true
	}
	return rp( opt )
	.then(function( body ){
		$ = cheerio.load( body );
		var avatarEl = $('#p_buddy').find('img');
		var avatar = avatarEl.attr('src').replace(/@.*/,'@1e_1c_0i_1o_100Q_200w_200h.jpg');
		var nickname = avatarEl.attr('alt');
		var data = {
			uid: uid,
			avatar: avatar,
			nickname: nickname
		};
		return data;
	})
	.catch(function(e){
		console.log((new Date()).toString() + e);
		return e;
	});
}
function isEmptyObject(obj) {
  for (var key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      return false;
    }
  }
  return true;
}
